﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using MiniFox.Configurations;
using MiniFox.Resources;
using MiniFox.Utility;
using MiniFox.Web.Configurations;

namespace MiniFox.Web
{
    /// <summary>
    /// 
    /// </summary>
    public static class WebExtensions
    {
        internal const string LOGGER_NAME = "MINIFOX_WEB_LOG";

        ///// <summary>
        ///// 
        ///// </summary>
        //internal static HostConfiguration HostingConfiguration
        //{
        //    get
        //    {
        //        return ConfigurationFactory.GetConfig<HostConfiguration>();
        //    }
        //}

        #region GetRouteValueDictionary
        /// <summary>
        /// 
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public static RouteValueDictionary GetRouteValueDictionary(IQueryCollection query)
        {
            RouteValueDictionary dict = new RouteValueDictionary();
            foreach (string key in query.Keys)
            {
                string val = query[key];
                dict.Add(key, val);
            }
            return dict;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static RouteValueDictionary GetRouteValueDictionary(this HttpRequest request)
        {
            RouteValueDictionary dict = GetRouteValueDictionary(request.Query);
            return dict;
        }
        #endregion

        #region CookieSerializable

        private static string CookieToJsonString(IRequestCookieCollection cookies)
        {
            if (cookies != null && cookies.Count > 0)
            {
                List<string> strlist = new List<string>();
                foreach (string key in cookies.Keys)
                {
                    var val = cookies[key];
                    strlist.Add(string.Format(JsonUtility.JSON_NODE_FORMAT, key, val));
                }
                return "{" + string.Join(",", strlist.ToArray()) + "}";
            }
            else
            {
                return string.Empty;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cookies"></param>
        /// <returns></returns>
        public static string ToJsonString(this IRequestCookieCollection cookies)
        {
            return CookieToJsonString(cookies);
        }
        #endregion

        #region is ajax
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static bool IsAjaxRequest(this HttpRequest request)
        {
            bool isAjax = !string.IsNullOrEmpty(request.Headers["x-requested-with"]) && string.Equals(request.Headers["x-requested-with"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase);
            return isAjax;
        }
        #endregion

        #region GetBodyContent

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetHttpBodyContentByRequest(HttpRequest request)
        {
            string requestBody = string.Empty;

            if ((request != null) && !string.Equals(request.Method, System.Net.WebRequestMethods.Http.Get, StringComparison.OrdinalIgnoreCase))
            {
                var stream = request.Body;
                if (stream.CanRead && stream.CanSeek)
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        long position = reader.BaseStream.Position;
                        reader.BaseStream.Position = 0;
                        requestBody = reader.ReadToEnd();
                        reader.BaseStream.Position = position;
                        reader.Close();
                    }
            }
            return requestBody;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetHttpBodyContent(this HttpRequest request)
        {
            return GetHttpBodyContentByRequest(request);
        }
        #endregion

        #region GetClientHostNameByRequest

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetClientHostNameByRequest(HttpRequest request)
        {

            string clientHostName = request.HttpContext.GetServerVariable("REMOTE_ADDR");
            if (string.IsNullOrWhiteSpace(clientHostName))
            {
                clientHostName = request.HttpContext.Connection.RemoteIpAddress?.ToString();
            }
            return clientHostName;
        }
        #endregion

        #region GetClientHostName

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetClientHostName(this HttpRequest request)
        {
            return GetClientHostNameByRequest(request);
        }
        #endregion

        #region GetClientIpByRequest

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetClientIpByRequest(HttpRequest request)
        {
            string clientIp = request.Headers["HTTP_X_FORWARDED_FOR"];
            if (string.IsNullOrWhiteSpace(clientIp))
            {
                clientIp = request.Headers["REMOTE_ADDR"];
            }
            if (string.IsNullOrWhiteSpace(clientIp))
            {
                clientIp = request.HttpContext.Connection.RemoteIpAddress?.ToString();
            }
            return clientIp;
        }

        #endregion

        #region GetClientIp
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetClientIP(this HttpRequest request)
        {
            return GetClientIpByRequest(request);
        }
        #endregion

        #region GetFullApplicationPathByRequest

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetFullApplicationPathByRequest(HttpRequest request)
        {
            return request.GetFullHost() + (request.PathBase == "/" ? string.Empty : (request.PathBase.HasValue ? request.PathBase.Value.TrimEnd('/') : string.Empty));
        }
        #endregion

        #region GetFullApplicationPath
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetFullApplicationPath(this HttpRequest request)
        {
            return GetFullApplicationPathByRequest(request);
        }
        #endregion

        #region GetFullRawUrlByRequest
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetFullRawUrlByRequest(HttpRequest request)
        //{
        //    return request.GetEncodedUrl();
        //}
        #endregion

        #region GetFullRawUrl
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetFullRawUrl(this HttpRequest request)
        //{
        //    return GetFullRawUrlByRequest(request);
        //}

        #endregion

        #region GetFullAbsolutionPathByRequest
        /// <summary>
        /// 获取绝对路径，但不包含querystring
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetFullAbsolutionPathByRequest(HttpRequest request)
        {
            return request.GetFullHost() + request.Path;
        }
        #endregion

        #region GetFullAbsolutionPath
        /// <summary>
        /// 获取绝对路径，但不包含querystring
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetFullAbsolutionPath(this HttpRequest request)
        {
            return GetFullAbsolutionPathByRequest(request);
        }
        #endregion

        #region GetFullHostByRequest
        /// <summary>
        /// input "http://www.xxx.com/default.htm" will return "http://www.xxx.com"
        /// input "http://www.xxx.com:8080/default.htm" will return "http://www.xxx.com:8080"
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetFullHostByRequest(HttpRequest request)
        {
            if (request == null)
                return string.Empty;

            return $"{request.Scheme}://{request.Host}";
        }
        #endregion

        #region GetFullHost
        /// <summary>
        /// input "http://www.xxx.com/default.htm" will return "http://www.xxx.com"
        /// input "https://www.xxx.com:8080/default.htm" will return "https://www.xxx.com:8080"
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetFullHost(this HttpRequest request)
        {
            return GetFullHostByRequest(request);
        }
        #endregion

        //#region GetProtocalByRequest
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetProtocalByRequest(HttpRequest request)
        //{
        //    return request.Protocol;
        //}
        //#endregion

        //#region GetProtocal
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetProtocal(this HttpRequest request)
        //{
        //    return GetProtocalByRequest(request);
        //}
        //#endregion

        //#region GetSchemeByRequest
        ///// <summary>
        ///// input "http://www.xxx.com/default.htm" will return "http",
        ///// input "https://sub.xxx.com:8080/default.htm" will return "https"
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetSchemeByRequest(HttpRequest request)
        //{
        //    if (request == null)
        //        return string.Empty;
        //    var hosting = HostingConfiguration;
        //    if (hosting == null || string.IsNullOrWhiteSpace(hosting.Scheme))
        //        return request.Scheme;
        //    return hosting.Scheme;
        //}
        //#endregion

        //#region GetScheme
        ///// <summary>
        ///// input "http://www.xxx.com/default.htm" will return "http",
        ///// input "https://sub.xxx.com:8080/default.htm" will return "https"
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetScheme(this HttpRequest request)
        //{
        //    return GetSchemeByRequest(request);
        //}
        //#endregion

        //#region GetHostByRequest
        ///// <summary>
        ///// input "http://www.xxx.com/default.htm" will return "www.xxx.com"
        ///// input "http://sub.xxx.com:8080/default.htm" will return "sub.xxx.com"
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetHostByRequest(HttpRequest request)
        //{
        //    if (request == null)
        //        return string.Empty;
        //    var hosting = HostingConfiguration;
        //    if (hosting == null || string.IsNullOrEmpty(hosting.HostName))
        //        return request.Host.Host;
        //    return hosting.HostName;
        //}
        //#endregion

        //#region GetHost
        ///// <summary>
        ///// input "http://www.xxx.com/default.htm" will return "www.xxx.com",
        ///// input "http://sub.xxx.com:8080/default.htm" will return "sub.xxx.com"
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static string GetHost(this HttpRequest request)
        //{
        //    return GetHostByRequest(request);
        //}
        //#endregion

        //#region GetPortByRequest
        ///// <summary>
        ///// input "http://www.xxx.com/default.htm" will return "80",
        ///// input "https://sub.xxx.com:8080/default.htm" will return "8080"
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static int GetPortNumberByRequest(HttpRequest request)
        //{
        //    if (request == null)
        //        throw new InvalidOperationException();
        //    var hosting = HostingConfiguration;
        //    if (hosting == null || hosting.Undefined)
        //        return request.Host.Port ?? (request.IsHttps ? 443 : 80);

        //    return hosting.Port;
        //}
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="request"></param>
        ///// <param name="showDefaultAsNull"></param>
        ///// <returns></returns>
        //public static string GetPortByRequest(HttpRequest request, bool showDefaultAsNull = false)
        //{
        //    if (request == null)
        //        throw new InvalidOperationException();
        //    int port = request.GetPortNumber();
        //    int defaultPort;
        //    string scheme;
        //    var hosting = HostingConfiguration;
        //    if (hosting == null || hosting.Undefined)
        //    {
        //        scheme = request.Scheme;
        //    }
        //    else
        //    {
        //        scheme = hosting.Scheme;
        //    }

        //    switch (scheme)
        //    {
        //        case "https":
        //            defaultPort = 443;
        //            break;
        //        case "http":
        //            defaultPort = 80;
        //            break;
        //        default:
        //            defaultPort = 80;
        //            break;
        //    }
        //    if (port == defaultPort && showDefaultAsNull)
        //        return string.Empty;
        //    return port.ToString();
        //}
        //#endregion

        //#region GetPort
        ///// <summary>
        ///// input "http://www.xxx.com/default.htm" will return "80",
        ///// input "https://sub.xxx.com:8080/default.htm" will return "8080"
        ///// </summary>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public static int GetPortNumber(this HttpRequest request)
        //{
        //    return GetPortNumberByRequest(request);
        //}
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="request"></param>
        ///// <param name="showDefaultAsNull"></param>
        ///// <returns></returns>
        //public static string GetPort(this HttpRequest request, bool showDefaultAsNull = false)
        //{
        //    return GetPortByRequest(request, showDefaultAsNull);
        //}
        //#endregion

        #region get file

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static IEnumerable<(string name, byte[] data, string fileName, string mimeType, string extName)> GetHttpFileStream(this HttpRequest request)
        {
            if (request.Form.Files.Count <= 0)
                throw new MiniFox.Exceptions.SystemException(CommonMessages.MSG_SysError);
            List<Task<(string name, byte[] data, string fileName, string mimeType, string extName)>> list = new List<Task<(string name, byte[] data, string fileName, string mimeType, string extName)>>();
            
            foreach(var uploadfile in request.Form.Files)
            {
                var task = Task.Run(() =>
                {
                    var (name, data, fileName, mimeType, extName) = GetHttpFileStream(uploadfile);
                    return (name, data, fileName, mimeType, extName);
                });
                list.Add(task);

            }
            Task.WaitAll(list.ToArray());
            return list.Select(t => t.Result);
        }

        private static (string name, byte[] data, string fileName, string mimeType, string extName) GetHttpFileStream(IFormFile uploadfile)
        {
            MemoryStream ms = new MemoryStream();
            uploadfile.CopyTo(ms);
            byte[] data = ms.ToArray();

            string name = uploadfile.Name;
            string fileName = uploadfile.FileName;
            string mimeType = uploadfile.ContentType;
            var arr = fileName.Split('.');
            string extName = arr.Length > 1 ? arr.Last() : string.Empty;
            return (name, data, fileName, mimeType, extName);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static async Task<IEnumerable<(string name, byte[] data, string fileName, string mimeType, string extName)>> GetHttpFileStreamAsync(this HttpRequest request)
        {
            if (request.Form.Files.Count <= 0)
                throw new MiniFox.Exceptions.SystemException(CommonMessages.MSG_SysError);
            List<Task<(string name, byte[] data, string fileName, string mimeType, string extName)>> list = new List<Task<(string name, byte[] data, string fileName, string mimeType, string extName)>>();

            foreach (var uploadfile in request.Form.Files)
            {
                list.Add(GetHttpFileStreamAsync(uploadfile));
            }
            return await Task.WhenAll(list);
        }

        private static async Task<(string name, byte[] data, string fileName, string mimeType, string extName)> GetHttpFileStreamAsync(IFormFile uploadfile)
        {
            MemoryStream ms = new MemoryStream();
            await uploadfile.CopyToAsync(ms);
            byte[] data = ms.ToArray();

            string name = uploadfile.Name;
            string fileName = uploadfile.FileName;
            string mimeType = uploadfile.ContentType;
            var arr = fileName.Split('.');
            string extName = arr.Length > 1 ? arr.Last() : string.Empty;
            return (name, data, fileName, mimeType, extName);
        }

        #endregion
    }
}
